import java.util.ArrayList;



public class IterativeDeepeningDepthLimitedMinimaxSearchEngine implements SearchEngine {

	private static final int DEPTH_LIMIT = 50;
	
	private Node root;
	private MovePerformer bestMove = null;
	
	public IterativeDeepeningDepthLimitedMinimaxSearchEngine(Board board) {		
		root = new Node();
		root.setBoard(board);
		int player = Node.MAX_PLAYER;
		if(board.getPlayerPieces().get(0).getColor() == Piece.COLOR_BLACK) player = Node.MIN_PLAYER;
		root.setPlayer(player);
		root.setLevel(0);
	}
	
	// Sometimes a move cannot be found to be performed mainly because the check situation
	// causes a lot of pieces to be unable to move. Probably there is a problem at the
	// function that generates the possible moves for each piece when considering the
	// check situation.
	@Override
	public void execute(Move[] moves, Piece[] pieces, int playerColor) {
		root.getBoard().setMovesArray(moves);
		
		root.expand();

		int depth = 1;
		while(true){
			minimaxEval(root, depth);
			if(getBestMove() == null){
				System.out.println("Could not find a move with depth " + depth);
				if(depth == DEPTH_LIMIT) break;
				depth++;
			}else{
				System.out.print("Found a move with depth " + depth + " -> ");
				getBestMove().performMove();
				break;
			}
		}
	}
	
	public double minimaxEval(Node node, int depthLeft) {
		MovePerformer localBestMove = null;
		boolean maximizing = (node.getPlayer() == Node.MAX_PLAYER);
		double bestUtility = maximizing ? Double.NEGATIVE_INFINITY : Double.POSITIVE_INFINITY;

		// Return utility if game over or depth limit reached
		if (node.isGoal() || depthLeft == 0){			
			return node.utility();
		}

		// Otherwise, generate children
		ArrayList<Node> children = node.expand();

		// Evaluate the depth-limited mini-max value for each
		for (Node child : children) {
			double childUtility = minimaxEval(child, depthLeft - 1);
			// update best utility and move if appropriate
			if (maximizing == childUtility > bestUtility) {
				bestUtility = childUtility;
				localBestMove = child.getUsedMove();
			}
		}

		// Before returning utility, assign local best move to
		// instance variable.  The last value assigned in the
		// recursive evaluation will be the best move from the
		// root node.
		bestMove = localBestMove;
		return bestUtility;
	}
	
	public MovePerformer getBestMove() {
		return bestMove;
	}

}
